/*
 * StreamPlayer.java
 *
 * 08/11/2009
 */
package kg.sound.player.media;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;

/**
 * @author fernando
 */
public class StreamPlayer extends Player {

	private final static Logger LOGGER = Logger.getLogger(StreamPlayer.class
			.getName());

	private final MediaWrapper media;
	private SourceDataLine source;
	private boolean stop;

	public StreamPlayer(MediaWrapper media) throws Exception {
		this.media = media;
	}

	private SourceDataLine getSource() {
		return source;
	}

	private void setSource(SourceDataLine source) {
		this.source = source;
	}

	@Override
	protected void start() throws Exception {
		setSource(createSource());
		getSource().addLineListener(media.getLineListener());
		getSource().start();

		/* Playing the media in other thread so we don't get the system blocked */
		new Thread(new Runnable() {
			public void run() {
				
				/* Read and play chunks of the audio */
				try {
					byte[] buffer = new byte[getSource().getBufferSize()];
					int numRead = 0;
					
					while (!stop
							&& ((numRead = stream.read(buffer, 0,
									buffer.length)) != -1)) {
						getSource().write(buffer, 0, numRead);
					}

					/* Wait until all data is played, then close the line */
					getSource().drain();

				} catch (Throwable th) {
					LOGGER.log(Level.SEVERE, "An exception has ocurred "
							+ "while playing the media", th);

				} finally {
					getSource().stop();
					try {
						stream.close();
					} catch (IOException e) {
						LOGGER.log(Level.SEVERE, "An exception has ocurred "
								+ "while trying to close the stream", e);
					}
				}
			}
		}).start();
	}
	
	private SourceDataLine createSource() throws Exception {
		AudioFormat format = stream.getFormat();

		/* Gather information for line creation */
		DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);

		/* Check whether line is supported */
		if (!AudioSystem.isLineSupported(info)) {
			throw new RuntimeException("Line is not support for that media "
					+ "type: " + format.toString());
		}

		/* Create an empty clip using that line information */
		SourceDataLine source = (SourceDataLine) AudioSystem.getLine(info);

		/* Open the audio stream; now it's ready to play */
		source.open(format);

		return source;
	}

	@Override
	void stop() {
		stop = true;
	}

	@Override
	void purge() {
		try {
			getSource().removeLineListener(media.getLineListener());
			getSource().close();
		} catch (Exception ex) {
			LOGGER.log(Level.SEVERE, "An exception has ocurred "
					+ "while trying to close the media", ex);
		}
		setSource(null);
	}
}